<template>
  <view class="u-calendar-month-wrapper" ref="u-calendar-month-wrapper">
    <view
      v-for="(item, index) in showmonths"
      :key="index"
      :class="[`u-calendar-month-${index}`]"
      :ref="`u-calendar-month-${index}`"
      :id="`month-${index}`"
    >
      <text v-if="index !== 0" class="u-calendar-month__title">
        {{ item.year }}年{{ item.month }}月
      </text>
      <view class="u-calendar-month__days">
        <view
          v-if="showMark"
          class="u-calendar-month__days__month-mark-wrapper"
        >
          <text class="u-calendar-month__days__month-mark-wrapper__text">
            {{ item.month }}
          </text>
        </view>
        <view
          class="u-calendar-month__days__day"
          v-for="(item1, index1) in item.date"
          :key="index1"
          :style="[dayStyle(index, index1, item1)]"
          @tap="clickHandler(index, index1, item1)"
          :class="[
            item1.selected && 'u-calendar-month__days__day__select--selected',
          ]"
        >
          <view
            class="u-calendar-month__days__day__select"
            :style="[daySelectStyle(index, index1, item1)]"
          >
            <text
              class="u-calendar-month__days__day__select__info"
              :class="[
                item1.disabled &&
                  'u-calendar-month__days__day__select__info--disabled',
              ]"
              :style="[textStyle(item1)]"
            >
              {{ item1.day }}
            </text>
            <text
              v-if="getBottomInfo(index, index1, item1)"
              class="u-calendar-month__days__day__select__buttom-info"
              :class="[
                item1.disabled &&
                  'u-calendar-month__days__day__select__buttom-info--disabled',
              ]"
              :style="[textStyle(item1)]"
            >
              {{ getBottomInfo(index, index1, item1) }}
            </text>
            <text
              v-if="item1.dot"
              class="u-calendar-month__days__day__select__dot"
              :style="[dotStyle(item1)]"
            ></text>
            <view v-if="item1.Holiday||item1.type=='04'" style="position: absolute; top: 0px; right: -14px">
              <u-badge value="双倍"></u-badge>
            </view>
          </view>
        </view>
      </view>
    </view>
  </view>
</template>

<script>
import { signInRecordHoliday } from "@/api/points.js";
// 
// #ifdef APP-NVUE
// 由于nvue不支持百分比单位，需要查询宽度来计算每个日期的宽度
const dom = uni.requireNativePlugin("dom");
// #endif
import dayjs from "../../libs/util/dayjs.js";
export default {
  name: "u-calendar-month",
  mixins: [uni.$u.mpMixin, uni.$u.mixin],
  props: {
    list: {
      type: Array,
      default: () => [],
    },
    // 是否显示月份背景色
    showMark: {
      type: Boolean,
      default: true,
    },
    // 主题色，对底部按钮和选中日期有效
    color: {
      type: String,
      default: "#39C03E",
    },
    // 月份数据
    months: {
      type: Array,
      default: () => [],
    },
    // 日期选择类型
    mode: {
      type: String,
      default: "single",
    },
    // 日期行高
    rowHeight: {
      type: [String, Number],
      default: 58,
    },
    // mode=multiple时，最多可选多少个日期
    maxCount: {
      type: [String, Number],
      default: Infinity,
    },
    // mode=range时，第一个日期底部的提示文字
    startText: {
      type: String,
      default: "开始",
    },
    // mode=range时，最后一个日期底部的提示文字
    endText: {
      type: String,
      default: "结束",
    },
    // 默认选中的日期，mode为multiple或range是必须为数组格式
    defaultDate: {
      type: [Array, String, Date, Number],
      default: null,
    },
    // 最小的可选日期
    minDate: {
      type: [String, Number],
      default: 0,
    },
    // 最大可选日期
    maxDate: {
      type: [String, Number],
      default: 0,
    },
    // 如果没有设置maxDate，则往后推多少个月
    maxMonth: {
      type: [String, Number],
      default: 2,
    },
    // 是否为只读状态，只读状态下禁止选择日期
    readonly: {
      type: Boolean,
      default: uni.$u.props.calendar.readonly,
    },
    // 日期区间最多可选天数，默认无限制，mode = range时有效
    maxRange: {
      type: [Number, String],
      default: Infinity,
    },
    // 范围选择超过最多可选天数时的提示文案，mode = range时有效
    rangePrompt: {
      type: String,
      default: "",
    },
    // 范围选择超过最多可选天数时，是否展示提示文案，mode = range时有效
    showRangePrompt: {
      type: Boolean,
      default: true,
    },
    // 是否允许日期范围的起止时间为同一天，mode = range时有效
    allowSameDay: {
      type: Boolean,
      default: false,
    },
    selectedxs: {
      type: Array,
      default: () => {
        return [];
      },
    },
  },
  data() {
    return {
      // 每个日期的宽度
      width: 0,
      // 当前选中的日期item
      item: {},
      Holiday: false,
      // selected: [],
    };
  },
  watch: {
    selectedChange: {
      immediate: true,
      handler(n) {
        this.setDefaultDate();
      },
    },
    selectedxs(){
      this.setItem(this.selected)
    },
    showmonths(){
      this.setItem(this.selected)
    }
  },
 
  computed: {

    showmonths() {
      let list = this.list;
      let datedq = dayjs().format("YYYY-MM-DD");
      let r = this.months.map((e) => {
        e.date = e.date.map((e2) => {
          let t = this.$u.timeFormat(e2.date, "yyyy-mm-dd");

          let dot = list.find((e3) => {
            return t == e3.t;
          });
          e2.dot = dot ? true : false;
          e2.type = dot ? dot.type : '';
          if (datedq == t) {
            e2.Holiday=this.Holiday
          }
          e2.status=datedq==t?0:dayjs(e2.date).isAfter(dayjs())?1:-1
          return e2;
        });
        return e;
      });
      return r;
    },
    selected() {
      return this.selectedxs || [];
    },
    // 多个条件的变化，会引起选中日期的变化，这里统一管理监听
    selectedChange() {
      return [this.minDate, this.maxDate, this.defaultDate];
    },
    dayStyle(index1, index2, item) {
      return (index1, index2, item) => {
        const style = {};
        let week = item.week;
        // 不进行四舍五入的形式保留2位小数
        const dayWidth = Number(
          parseFloat(this.width / 7)
            .toFixed(3)
            .slice(0, -1)
        );
        // 得出每个日期的宽度
        // #ifdef APP-NVUE
        style.width = uni.$u.addUnit(dayWidth);
        // #endif
        style.height = uni.$u.addUnit(this.rowHeight);
        if (index2 === 0) {
          // 获取当前为星期几，如果为0，则为星期天，减一为每月第一天时，需要向左偏移的item个数
          week = week;
          style.marginLeft = uni.$u.addUnit(week * dayWidth);
        }
        if (this.mode === "range") {
          // 之所以需要这么写，是因为DCloud公司的iOS客户端的开发者能力有限导致的bug
          style.paddingLeft = 0;
          style.paddingRight = 0;
          style.paddingBottom = 0;
          style.paddingTop = 0;
        }
        return style;
      };
    },
    daySelectStyle() {
      return (index1, index2, item) => {
        let date = dayjs(item.date).format("YYYY-MM-DD"),
          style = {};
        // 判断date是否在selected数组中，因为月份可能会需要补0，所以使用dateSame判断，而不用数组的includes判断
        let datedq = dayjs().format("YYYY-MM-DD");
        if (datedq == date) {
          style.backgroundColor = "rgba(57, 192, 62, 0.20)";
          style.borderTopLeftRadius = "3px";
          style.borderBottomLeftRadius = "3px";
          style.borderTopRightRadius = "3px";
          style.borderBottomRightRadius = "3px";
          style.color = "#39C03E";
          
        }
        if (this.selected?.some((item) => this.dateSame(item, date))) {
          style.backgroundColor = "#39C03E";
        }
        if (this.mode === "single") {
          if (date === this.selected[0]) {
            // 因为需要对nvue的兼容，只能这么写，无法缩写，也无法通过类名控制等等
            style.borderTopLeftRadius = "3px";
            style.borderBottomLeftRadius = "3px";
            style.borderTopRightRadius = "3px";
            style.borderBottomRightRadius = "3px";
          }
        } else if (this.mode === "range") {
          if (this.selected.length >= 2) {
            const len = this.selected.length - 1;
            // 第一个日期设置左上角和左下角的圆角
            if (this.dateSame(date, this.selected[0])) {
              style.borderTopLeftRadius = "3px";
              style.borderBottomLeftRadius = "3px";
            }
            // 最后一个日期设置右上角和右下角的圆角
            if (this.dateSame(date, this.selected[len])) {
              style.borderTopRightRadius = "3px";
              style.borderBottomRightRadius = "3px";
            }
            // 处于第一和最后一个之间的日期，背景色设置为浅色，通过将对应颜色进行等分，再取其尾部的颜色值
            if (
              dayjs(date).isAfter(dayjs(this.selected[0])) &&
              dayjs(date).isBefore(dayjs(this.selected[len]))
            ) {
              style.backgroundColor = uni.$u.colorGradient(
                this.color,
                "#ffffff",
                100
              )[90];
              // 增加一个透明度，让范围区间的背景色也能看到底部的mark水印字符
              style.opacity = 0.7;
            }
          } else if (this.selected.length === 1) {
            // 之所以需要这么写，是因为DCloud公司的iOS客户端的开发者能力有限导致的bug
            // 进行还原操作，否则在nvue的iOS，uni-app有bug，会导致诡异的表现
            style.borderTopLeftRadius = "3px";
            style.borderBottomLeftRadius = "3px";
          }
        } else {
          if (this.selected.some((item) => this.dateSame(item, date))) {
            style.borderTopLeftRadius = "3px";
            style.borderBottomLeftRadius = "3px";
            style.borderTopRightRadius = "3px";
            style.borderBottomRightRadius = "3px";
          }
        }
        return style;
      };
    },
    // 某个日期是否被选中
    textStyle() {
      return (item) => {
        const date = dayjs(item.date).format("YYYY-MM-DD"),
          style = {};
        // 选中的日期，提示文字设置白色
        if (this.selected.some((item) => this.dateSame(item, date))) {
          style.color = "#ffffff";
        }
        if (this.mode === "range") {
          const len = this.selected.length - 1;
          // 如果是范围选择模式，第一个和最后一个之间的日期，文字颜色设置为高亮的主题色
          if (
            dayjs(date).isAfter(dayjs(this.selected[0])) &&
            dayjs(date).isBefore(dayjs(this.selected[len]))
          ) {
            style.color = this.color;
          }
        }
        return style;
      };
    },
    dotStyle() {
      return (item) => {
        const date = dayjs(item.date).format("YYYY-MM-DD"),
          style = {};
        // 选中的日期，提示文字设置白色
        if (this.selected.some((item) => this.dateSame(item, date))) {
          style.backgroundColor = "#ffffff";
        }
        if (this.mode === "range") {
          const len = this.selected.length - 1;
          // 如果是范围选择模式，第一个和最后一个之间的日期，文字颜色设置为高亮的主题色
          if (
            dayjs(date).isAfter(dayjs(this.selected[0])) &&
            dayjs(date).isBefore(dayjs(this.selected[len]))
          ) {
            // style.color = this.color;
          }
        }
        return style;
      };
    },
    // 获取底部的提示文字
    getBottomInfo() {
      return (index1, index2, item) => {
        const date = dayjs(item.date).format("YYYY-MM-DD");
        const bottomInfo = item.bottomInfo;
        // 当为日期范围模式时，且选择的日期个数大于0时
        if (this.mode === "range" && this.selected.length > 0) {
          if (this.selected.length === 1) {
            // 选择了一个日期时，如果当前日期为数组中的第一个日期，则显示底部文字为“开始”
            if (this.dateSame(date, this.selected[0])) return this.startText;
            else return bottomInfo;
          } else {
            const len = this.selected.length - 1;
            // 如果数组中的日期大于2个时，第一个和最后一个显示为开始和结束日期
            if (
              this.dateSame(date, this.selected[0]) &&
              this.dateSame(date, this.selected[1]) &&
              len === 1
            ) {
              // 如果长度为2，且第一个等于第二个日期，则提示语放在同一个item中
              return `${this.startText}/${this.endText}`;
            } else if (this.dateSame(date, this.selected[0])) {
              return this.startText;
            } else if (this.dateSame(date, this.selected[len])) {
              return this.endText;
            } else {
              return bottomInfo;
            }
          }
        } else {
          return bottomInfo;
        }
      };
    },
  },
  mounted() {
    this.init();
  },
  methods: {
   async init() {
    
      // 初始化默认选中
      // this.$emit("monthSelected", this.selected);
      this.$nextTick(() => {
        // 这里需要另一个延时，因为获取宽度后，会进行月份数据渲染，只有渲染完成之后，才有真正的高度
        // 因为nvue下，$nextTick并不是100%可靠的
        uni.$u.sleep(10).then(() => {
          this.getWrapperWidth();
          this.getMonthRect();
        });
      });
      let data=await signInRecordHoliday()
      // console.log(data,"data");
      this.Holiday=data.data
    },
    // 判断两个日期是否相等
    dateSame(date1, date2) {
      return dayjs(date1).isSame(dayjs(date2));
    },
    // 获取月份数据区域的宽度，因为nvue不支持百分比，所以无法通过css设置每个日期item的宽度
    getWrapperWidth() {
      // #ifdef APP-NVUE
      dom.getComponentRect(this.$refs["u-calendar-month-wrapper"], (res) => {
        this.width = res.size.width;
      });
      // #endif
      // #ifndef APP-NVUE
      this.$uGetRect(".u-calendar-month-wrapper").then((size) => {
        this.width = size.width;
      });
      // #endif
    },
    getMonthRect() {
      // 获取每个月份数据的尺寸，用于父组件在scroll-view滚动事件中，监听当前滚动到了第几个月份
      const promiseAllArr = this.months.map((item, index) =>
        this.getMonthRectByPromise(`u-calendar-month-${index}`)
      );
      // 一次性返回
      Promise.all(promiseAllArr).then((sizes) => {
        let height = 1;
        const topArr = [];
        for (let i = 0; i < this.months.length; i++) {
          // 添加到months数组中，供scroll-view滚动事件中，判断当前滚动到哪个月份
          topArr[i] = height;
          height += sizes[i].height;
        }
        // 由于微信下，无法通过this.months[i].top的形式(引用类型)去修改父组件的month的top值，所以使用事件形式对外发出
        this.$emit("updateMonthTop", topArr);
      });
    },
    // 获取每个月份区域的尺寸
    getMonthRectByPromise(el) {
      // #ifndef APP-NVUE
      // $uGetRect为uView自带的节点查询简化方法，详见文档介绍：https://www.uviewui.com/js/getRect.html
      // 组件内部一般用this.$uGetRect，对外的为uni.$u.getRect，二者功能一致，名称不同
      return new Promise((resolve) => {
        this.$uGetRect(`.${el}`).then((size) => {
          resolve(size);
        });
      });
      // #endif

      // #ifdef APP-NVUE
      // nvue下，使用dom模块查询元素高度
      // 返回一个promise，让调用此方法的主体能使用then回调
      return new Promise((resolve) => {
        dom.getComponentRect(this.$refs[el][0], (res) => {
          resolve(res.size);
        });
      });
      // #endif
    },
    // 点击某一个日期
    clickHandler(index1, index2, item) {
      if (this.readonly) {
        return;
      }
      this.item = item;
      const date = dayjs(item.date).format("YYYY-MM-DD");

      if (item.disabled) return;
      // 对上一次选择的日期数组进行深度克隆
      let selected = uni.$u.deepClone(this.selected);
      if (this.mode === "single") {
        // 单选情况下，让数组中的元素为当前点击的日期
        selected = [date];
      } else if (this.mode === "multiple") {
        if (selected.some((item) => this.dateSame(item, date))) {
          // 如果点击的日期已在数组中，则进行移除操作，也就是达到反选的效果
          const itemIndex = selected.findIndex((item) => item === date);
          selected.splice(itemIndex, 1);
        } else {
          // 如果点击的日期不在数组中，且已有的长度小于总可选长度时，则添加到数组中去
          if (selected.length < this.maxCount) selected.push(date);
        }
      } else {
        // 选择区间形式
        if (selected.length === 0 || selected.length >= 2) {
          // 如果原来就为0或者大于2的长度，则当前点击的日期，就是开始日期
          selected = [date];
        } else if (selected.length === 1) {
          // 如果已经选择了开始日期
          const existsDate = selected[0];
          // 如果当前选择的日期小于上一次选择的日期，则当前的日期定为开始日期
          if (dayjs(date).isBefore(existsDate)) {
            selected = [date];
          } else if (dayjs(date).isAfter(existsDate)) {
            // 当前日期减去最大可选的日期天数，如果大于起始时间，则进行提示
            if (
              dayjs(dayjs(date).subtract(this.maxRange, "day")).isAfter(
                dayjs(selected[0])
              ) &&
              this.showRangePrompt
            ) {
              if (this.rangePrompt) {
                uni.$u.toast(this.rangePrompt);
              } else {
                uni.$u.toast(`选择天数不能超过 ${this.maxRange} 天`);
              }
              return;
            }
            // 如果当前日期大于已有日期，将当前的添加到数组尾部
            selected.push(date);
            const startDate = selected[0];
            const endDate = selected[1];
            const arr = [];
            let i = 0;
            do {
              // 将开始和结束日期之间的日期添加到数组中
              arr.push(dayjs(startDate).add(i, "day").format("YYYY-MM-DD"));
              i++;
              // 累加的日期小于结束日期时，继续下一次的循环
            } while (dayjs(startDate).add(i, "day").isBefore(dayjs(endDate)));
            // 为了一次性修改数组，避免computed中多次触发，这里才用arr变量一次性赋值的方式，同时将最后一个日期添加近来
            arr.push(endDate);
            selected = arr;
          } else {
            // 选择区间时，只有一个日期的情况下，且不允许选择起止为同一天的话，不允许选择自己
            if (selected[0] === date && !this.allowSameDay) return;
            selected.push(date);
          }
        }
      }
      this.setSelected(selected, 1);
    },
    // 设置默认日期
    setDefaultDate() {
      if (!this.defaultDate) {
        // 如果没有设置默认日期，则将当天日期设置为默认选中的日期
        const selected = [dayjs().format("YYYY-MM-DD")];
        return this.setSelected(selected, true);
      }
      let defaultDate = [];
      const minDate = this.minDate || dayjs().format("YYYY-MM-DD");
      const maxDate =
        this.maxDate ||
        dayjs(minDate)
          .add(this.maxMonth - 1, "month")
          .format("YYYY-MM-DD");
      if (this.mode === "single") {
        // 单选模式，可以是字符串或数组，Date对象等
        if (!uni.$u.test.array(this.defaultDate)) {
          defaultDate = [dayjs(this.defaultDate).format("YYYY-MM-DD")];
        } else {
          defaultDate = [this.defaultDate[0]];
        }
      } else {
        // 如果为非数组，则不执行
        if (!uni.$u.test.array(this.defaultDate)) return;
        defaultDate = this.defaultDate;
      }
      // 过滤用户传递的默认数组，取出只在可允许最大值与最小值之间的元素
      defaultDate = defaultDate.filter((item) => {
        return (
          dayjs(item).isAfter(dayjs(minDate).subtract(1, "day")) &&
          dayjs(item).isBefore(dayjs(maxDate).add(1, "day"))
        );
      });
      this.setSelected(defaultDate, false);
    },
    setItem(selected) { 
      let item=this.showmonths[0].date.find(item => dayjs(item.date).format("YYYY-MM-DD")==selected[0])
      if(item){
        this.$emit("setItem", item);
      }
     
    },    
    setSelected(selected, event = true) {
    
      selected = selected.filter((e) => e);
      // if (selected.length != "0") {
      //   this.selected = selected;
      // }
      event && this.$emit("monthSelected", selected);
    },
  },
};
</script>

<style lang="scss" scoped>
@import "../../libs/css/components.scss";

.u-calendar-month-wrapper {
  // margin-top: 4px;
}

.u-calendar-month {
  &__title {
    font-size: 14px;
    line-height: 42px;
    height: 42px;
    color: $u-main-color;
    text-align: center;
    font-weight: bold;
  }

  &__days {
    position: relative;
    @include flex;
    flex-wrap: wrap;

    &__month-mark-wrapper {
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      @include flex;
      justify-content: center;
      align-items: center;

      &__text {
        font-size: 155px;
        color: rgba(231, 232, 234, 0.83);
      }
    }

    &__day {
      @include flex;
      padding: 2px;
      /* #ifndef APP-NVUE */
      // vue下使用css进行宽度计算，因为某些安卓机会无法进行js获取父元素宽度进行计算得出，会有偏移
      width: calc(100% / 7);
      box-sizing: border-box;
      justify-content: center;
      /* #endif */

      &__select {
        // flex: 1;
        @include flex;
        align-items: center;
        justify-content: center;
        position: relative;
        width: 60rpx;

        &__dot {
          width: 8rpx;
          height: 8rpx;
          border-radius: 100px;
          background-color: #39c03e;
          position: absolute;
          top: 56rpx;
          right: 26rpx;
        }

        &__buttom-info {
          color: $u-content-color;
          text-align: center;
          position: absolute;
          bottom: 5px;
          font-size: 10px;
          text-align: center;
          left: 0;
          right: 0;

          &--selected {
            color: #ffffff;
          }

          &--disabled {
            color: #cacbcd;
          }
        }

        &__info {
          text-align: center;
          font-size: 16px;

          &--selected {
            color: #ffffff;
          }

          &--disabled {
            color: #cacbcd;
          }
        }

        &--selected {
          background-color: $u-primary;
          @include flex;
          justify-content: center;
          align-items: center;
          flex: 1;
          border-radius: 3px;
        }

        &--range-selected {
          opacity: 0.3;
          border-radius: 0;
        }

        &--range-start-selected {
          border-top-right-radius: 0;
          border-bottom-right-radius: 0;
        }

        &--range-end-selected {
          border-top-left-radius: 0;
          border-bottom-left-radius: 0;
        }
      }
    }
  }
}
</style>
